//
// RS Game Framework
// Copyright © 2009 Jedd Haberstro
// jhaberstro@gmail.com
// 
// $Id:
//

#include <rs/Mutex.hpp>
#include <rs/Thread.hpp>
#include <rs/unittest/UnitTest.hpp>
#include <vector>

namespace
{
    using namespace rs;
    using namespace rs::unittest;
        
    RS_TEST_CASE(rs::Mutex, Usage)
    {
        {
            Mutex mutex;
            mutex.Lock();
            for (int i = 0; i < 10; ++i) { }
            mutex.Unlock();
        }
        
        {
            Mutex mutex;
            Boolean success = mutex.TryLock();
            RS_ASSERT(success == true, "TryLock failed");
            for (int i = 0; i < 10; ++i) { }
            mutex.Unlock();
        }
    }
    
    
    enum { kSet = 0, kUnset = 1 };
    struct LockAndTryLockData { int flag; Mutex mutex; };
    
    void LockAndTryLockFunc(void* context) {
        LockAndTryLockData* data = static_cast< LockAndTryLockData* >(context);
        Boolean locked = data->mutex.TryLock();
        if (!locked) {
            data->flag = kSet;
        }
    }
    
    RS_TEST_CASE(rs::Mutex, LockAndTryLock)
    {
        LockAndTryLockData data;
        data.flag = kUnset;   
        data.mutex.Lock();
        {
            Thread thread(LockAndTryLockFunc);
            thread.Start(static_cast< void *>(&data));
            thread.Join();
            RS_ASSERT(data.flag == kSet, "flag was not set");
        }
        data.mutex.Unlock();
    }
    
    
    
    struct StaggeredLockingData
    {
        Mutex* mutex;
        int* counter;
    };
    
    void StaggeredLockingFunc(void* context) {
        StaggeredLockingData* data = static_cast< StaggeredLockingData* >(context);
        data->mutex->Lock();
        *data->counter = *data->counter + 1;
        data->mutex->Unlock();
    }
    
    RS_TEST_CASE(rs::Mutex, StaggeredLocking)
    {
        enum { kNumThreads = 20 };
        Mutex mutex;
        std::vector< Thread > threads;
        StaggeredLockingData data[kNumThreads];
        
        mutex.Lock();
        int counter = 0;
        
        for (int i = 0; i < kNumThreads; ++i) {
            threads.push_back(Thread(StaggeredLockingFunc));
        }

        for (int i = 0; i < kNumThreads; ++i) {
            data[i].mutex = &mutex;
            data[i].counter = &counter;
            threads[i].Start(static_cast< void * >(&data[i]));
        }
        mutex.Unlock();
        
        for (int i = 0; i < kNumThreads; ++i) {
            threads[i].Join();
        }
        
        RS_ASSERTF(counter == kNumThreads, "counter has incorrect value %i", counter);
    }
}